package server.core;

import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.concurrent.ExecutorService;

/**
 * The SocketWatcher class listens to the specified port number for incoming
 * connections and dispatches them to {@link ClientHandler} objects in a new
 * thread when they come in. It will loop until <code>stopWatcher</code> method
 * is invoked to halt it.
 * 
 * @author Clayton Edward Taylor
 */
public class SocketWatcher implements Runnable {

	private int serverPort;
	private ServerSocket serverSocket;
	private boolean isStopped;
	private ExecutorService threadPool;

	/**
	 * Creates a new SocketWatcher that will listen on the given port and
	 * run {@link ClientHandler} objects with the given thread pool. 
	 * 
	 * @param port       - The port number to listen for incoming connections
	 * @param threadPool - The thread pool to dispatch ClientHandlers to
	 * @see                SocketWatcher
	 */
	public SocketWatcher(int port, ExecutorService threadPool) {

		this.serverPort = port;
		this.isStopped = false;
		this.threadPool = threadPool;
	}

	/**
	 * Starts the SocketWatcher which will open up a socket on the desired port
	 * which it will listen for incoming connections on, dispatching them to
	 * {@link ClientHandler} objects run by the thread pool.
	 * 
	 * @throws RuntimeException If an unexpected socket error occurs
	 * @see    SocketWatcher
	 */
	@Override
	public void run() {

		try {
			serverSocket = new ServerSocket(serverPort);
		} catch (IOException e) {
			throw new RuntimeException("Error opening socket", e);
		}

		try {
			while (!isStopped()) {
				Socket clientSocket = serverSocket.accept();
				threadPool.execute(new ClientHandler(clientSocket));
			}
		} catch (IOException e) {
			if (!isStopped()) {
				throw new RuntimeException("Error accepting connection", e);
			}
		}
	}

	/**
	 * Halts the SocketWatcher immediately.
	 * 
	 * @see SocketWatcher
	 */
	public synchronized void stopWatcher() {
		
		threadPool.shutdownNow();
		this.isStopped = true;
		try {
			serverSocket.close();
		} catch (IOException e) {
			throw new RuntimeException("Error closing socket", e);
		}
	}

	/**
	 * Checks if the SocketWatcher has been stopped.
	 * 
	 * @return The current value of the <code>isStopped</code> flag
	 * @see    SocketWatcher
	 */
	private synchronized boolean isStopped() {
		
		return this.isStopped;
	}
}
